using System;
using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Pinwheel.Toolkits;

namespace Pinwheel.LSerialize
{
    public unsafe partial struct LStreamWriter
    {
    
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void WriteUnmanagedNumberField<T>(LSerializeContext* context, ref T value) where T : unmanaged, IEquatable<T>
        {
            // Profiler.Start("LSerialize.WriteUnmanagedNumberField Equals");
            // 为空或者default的时候跳过
            if (value.Equals(default))
            {
                // 递增Tag 
                context->IncreaseTag();
            }
            else
            {
                context->ResetValHeader(FieldType.Number);
                WriteUnmanagedNumberField<T>(context, context->ValHeader, ref value); 
            }
            // Profiler.End();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void WriteUnmanagedNumberField<T>(LSerializeContext* context, ValueHeader* header, ref T value)
        {
            // Profiler.Start("LStreamWriter.WriteUnmanagedNumberField");
            var offset = context->EnsureValOffset();
            Seek(offset, SeekOrigin.Begin);
            ValFormatter.Serialize(context, Ptr, ref *header);
            GetFormatter<T>().Serialize(context, Ptr, ref value);
            context->DecreaseSkip();
            context->IncreaseTag();
            if (null != context->RefHeader) context->RefHeader->ValCount++;
            if (0 == context->Skip) Seek(context->EnsureRefOffset(), SeekOrigin.Begin);
            // Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void WriteNextDepthBegin<T>(LSerializeContext* context)
        {
            Profiler.Start("LStreamWriter.WriteNextDepthBegin");
            // 递增深度
            context->IncreaseDepth(); 
            if (context->OutOfDepth) LSerializeException.ThrowReachedDepthLimit(typeof(T));
            Profiler.End();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal void WriteNextDepthEnd<T>(LSerializeContext* context)
        {
            Profiler.Start("LStreamWriter.WriteNextDepthEnd");
            var size = context->RefHeader->Size;
            // 递减深度
            context->Reset();
            context->DecreaseDepth();
            context->RefHeader->Size = Interlocked.Add(ref context->RefHeader->Size, size);
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public bool WriteManagedRefId<T>(LSerializeContext* context, int hash, out long refId)
        {
            Profiler.Start("LStreamWriter.WriteManagedRefId");
           
            // 检查Cache
            Profiler.Start("LStreamWriter.TryGetReference");
            bool find = LSerializeExtension.Cache.TryGetReference(ref hash, out refId);
            Profiler.End();
            
            Profiler.Start("LStreamWriter.CacheReference");
            if (!find)
            {
                refId = context->IncreaseRefId();
                LSerializeExtension.Cache.CacheReference(ref hash, ref refId);
            }
            Profiler.End();
            
            // 写入字段Guid
            Profiler.Start("LStreamWriter.ProcessTypeInfo");
            var type = typeof(T);
            var fieldType = type.GetFieldType();
            var collectionType = type.GetCollectionType();
            Profiler.End();
            
            context->ResetValHeader(fieldType, collectionType);
            WriteUnmanagedNumberField<long>(context, context->ValHeader, ref refId);

            Profiler.Start("LStreamWriter.SeekBegin");
            if (!find && context->Skip > 0)
            {
                var offset = context->EnsureRefOffset();
                Seek(offset, SeekOrigin.Begin);  
            }
            Profiler.End(); 
            
            Profiler.End();
            return find;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void WriteValueBegin(LSerializeContext* context, ref long refId)
        {
            Profiler.Start("LStreamWriter.WriteValueBegin");
            // 重置引用头部
            context->ResetRefHeader(true, refId);
            
            // 写入引用数据段
            // context->Head = Seek(0, SeekOrigin.Current);
            context->Head = Buffer->Index;
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void WriteValueEnd(LSerializeContext* context)
        {
            Profiler.Start("LStreamWriter.WriteValueEnd");
            var size = Seek(0, SeekOrigin.Current) - context->Head;
            context->RefHeader->Size = size;
            
            var tail = Seek(0, SeekOrigin.Current);
            
            // 写入引用头部
            Seek(context->Head, SeekOrigin.Begin);
            RefFormatter.Serialize(context, Ptr, ref *context->RefHeader);

            // 移动到引用数据段末尾
            Seek(tail, SeekOrigin.Begin);
            Profiler.End();
        }
        
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void WriteManagedField<T>(LSerializeContext* context, ref T value) where T : IEquatable<T>
        {
            // Profiler.Start("LStreamWriter.WriteManagedField");
            
            // 为空或者default的时候跳过
            Profiler.Start("LStreamWriter.WriteManagedField CheckNull");
            if (value.Equals(default))
            {
                // 递增Tag 
                context->IncreaseTag();
                Profiler.End();
                return;
            }
            Profiler.End();

            // 如果已经存在引用，直接返回
            if (!WriteManagedRefId<T>(context, value.GetHashCode(), out var refId))
            {
                // 如果不存在引用，递增RefCount
                if (null != context->RefHeader) context->RefHeader->RefCount++;
                WriteNextDepthBegin<T>(context);
                WriteManagedValue<T>(context, ref value, refId);
                WriteNextDepthEnd<T>(context);
            }
            
            // Profiler.End();
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void WriteManagedValue<T>(LSerializeContext* context, ref T value, long refId = 0L)
        {
            // Profiler.Start("LStreamWriter.WriteManagedValue");
            WriteValueBegin(context, ref refId);
            
            var headerIndex = Seek(sizeof(ReferenceHeader), SeekOrigin.Current); 
            var curValHeader = context->CurValHeader;
            if (null != value && null != curValHeader && curValHeader->ValType == FieldType.Class && curValHeader->CollectionType == CollectionType.None)
            {
                // Profiler.Start("LStreamWriter.WriteManagedValue Serialize Type");
                Type type = value.GetType();
                GetFormatter<Type>().Serialize(context, Ptr, ref type);
                // Profiler.End();
                // string typeName = type.FullName;
                // var sz = WriteString(typeName);
                
                // Profiler.Start("LStreamWriter.WriteManagedValue Serialize RealType");
                context->ValOffset = Seek(0, SeekOrigin.Current) - headerIndex;
                if (type.IsSubclassOf(typeof(T)))
                {
                    object v = value;
                    GetFormatter(type).Serialize(context, Ptr, ref v);
                    // value = (T) v;
                }
                else
                {
                    GetFormatter<T>().Serialize(context, Ptr, ref value);
                }
                // Profiler.End();
            }
            else
            {
                // Profiler.Start("LStreamWriter.WriteManagedValue Serialize BaseType");
                GetFormatter<T>().Serialize(context, Ptr, ref value); 
                // Profiler.End();
            }
            
            WriteValueEnd(context);
            // Profiler.End();
        }
    }
}
